package com.vegetable.common.aspect;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.vegetable.common.annotation.SalesOutChangeTracking;
import com.vegetable.modules.entity.inbound.OrderCost;
import com.vegetable.modules.entity.outbound.SalesOut;
import com.vegetable.modules.entity.outbound.SalesOutDetail;
import com.vegetable.modules.entity.stock.SalesOutChangeRecord;
import com.vegetable.modules.entity.sys.SysUserEntity;
import com.vegetable.modules.service.inbound.IOrderCostService;
import com.vegetable.modules.service.outbound.ISalesOutDetailService;
import com.vegetable.modules.service.stock.ISalesOutChangeRecordService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 销售出库变更追踪切面
 * 监控SalesOut对象的commodities和costs变化并记录
 * </p>
 *
 * @author system
 * @since 2025-10-23
 */
@Slf4j
@Aspect
@Component
@Order(100) // 设置切面优先级，数值越大优先级越低，确保在业务逻辑之后执行
public class SalesOutChangeTrackingAspect {

    @Autowired
    private ISalesOutDetailService salesOutDetailService;

    @Autowired
    private IOrderCostService orderCostService;

    @Autowired
    private ISalesOutChangeRecordService changeRecordService;

    // 用于防止切面重复进入的线程局部变量
    private static final ThreadLocal<Boolean> IN_TRACKING_ASPECT = new ThreadLocal<>();

    /**
     * 定义切点：拦截带有@SalesOutChangeTracking注解的方法
     */
    @Pointcut("@annotation(com.vegetable.common.annotation.SalesOutChangeTracking)")
    public void salesOutChangePointcut() {
    }

    /**
     * 环绕通知：记录SalesOut的commodities和costs变化
     */
    @Around("salesOutChangePointcut() && @annotation(salesOutChangeTracking)")
    public Object aroundSalesOutChange(ProceedingJoinPoint point, SalesOutChangeTracking salesOutChangeTracking) throws Throwable {
        log.info("进入销售出库变更追踪切面，方法: {}, 操作类型: {}", 
                point.getSignature().toShortString(), salesOutChangeTracking.type());

        // 检查是否已经在切面中，避免重复进入
        if (IN_TRACKING_ASPECT.get() != null && IN_TRACKING_ASPECT.get()) {
            log.debug("已在追踪切面中，跳过重复处理");
            return point.proceed();
        }

        try {
            // 标记已进入切面
            IN_TRACKING_ASPECT.set(true);

            // 1. 获取方法参数（SalesOut对象）
            Object[] args = point.getArgs();
            SalesOut salesOut = extractSalesOutFromArgs(args);

            if (salesOut == null) {
                log.warn("未能从方法参数中提取SalesOut对象，跳过变更追踪");
                return point.proceed();
            }

            // 2. 获取操作前的数据（仅在UPDATE操作时需要）
            DataSnapshot beforeSnapshot = null;
            if (salesOut.getId() != null && 
                (salesOutChangeTracking.type() == SalesOutChangeTracking.OperationType.SAVE ||
                 salesOutChangeTracking.type() == SalesOutChangeTracking.OperationType.SUBMIT ||
                 salesOutChangeTracking.type() == SalesOutChangeTracking.OperationType.UPDATE)) {
                beforeSnapshot = captureDataSnapshot(salesOut.getId());
            }

            // 3. 执行原方法
            Object result = point.proceed();

            // 4. 获取操作后的数据
            Long salesOutId = extractSalesOutIdFromResult(result, salesOut);
            if (salesOutId == null) {
                log.warn("未能获取销售出库单ID，跳过变更追踪");
                return result;
            }

            DataSnapshot afterSnapshot = captureDataSnapshot(salesOutId);

            // 5. 比对并记录变化
            recordChanges(salesOutId, salesOut.getDocNo(), beforeSnapshot, afterSnapshot, salesOutChangeTracking, salesOut);

            return result;
        } catch (Exception e) {
            log.error("销售出库变更追踪切面执行异常", e);
            // 即使切面异常，也不影响业务流程
            throw e;
        } finally {
            // 清理标记
            IN_TRACKING_ASPECT.remove();
        }
    }

    /**
     * 从方法参数中提取SalesOut对象
     */
    private SalesOut extractSalesOutFromArgs(Object[] args) {
        if (args == null || args.length == 0) {
            return null;
        }
        for (Object arg : args) {
            if (arg instanceof SalesOut) {
                return (SalesOut) arg;
            }
        }
        return null;
    }

    /**
     * 从方法返回结果中提取SalesOut ID
     */
    private Long extractSalesOutIdFromResult(Object result, SalesOut salesOut) {
        // 如果SalesOut对象已有ID，直接返回
        if (salesOut.getId() != null) {
            return salesOut.getId();
        }

        // 尝试从返回结果中提取ID
        if (result instanceof com.vegetable.common.utils.R) {
            com.vegetable.common.utils.R r = (com.vegetable.common.utils.R) result;
            Object data = r.getData();
            if (data instanceof Long) {
                return (Long) data;
            }
        }

        return null;
    }

    /**
     * 捕获数据快照
     */
    private DataSnapshot captureDataSnapshot(Long salesOutId) {
        DataSnapshot snapshot = new DataSnapshot();

        try {
            // 查询商品明细
            QueryWrapper<SalesOutDetail> detailQuery = new QueryWrapper<>();
            detailQuery.eq("sales_id", salesOutId);
            List<SalesOutDetail> details = salesOutDetailService.list(detailQuery);
            snapshot.setCommodities(details != null ? details : new ArrayList<>());

            // 查询其他费用
            List<OrderCost> costs = orderCostService.selectByMainId(salesOutId);
            snapshot.setCosts(costs != null ? costs : new ArrayList<>());

            log.debug("捕获数据快照 - 销售出库单ID: {}, 商品明细数: {}, 费用数: {}", 
                    salesOutId, snapshot.getCommodities().size(), snapshot.getCosts().size());
        } catch (Exception e) {
            log.error("捕获数据快照失败，销售出库单ID: {}", salesOutId, e);
        }

        return snapshot;
    }

    /**
     * 记录变化
     */
    private void recordChanges(Long salesOutId, String docNo, DataSnapshot beforeSnapshot, 
                               DataSnapshot afterSnapshot, SalesOutChangeTracking annotation,
                               SalesOut salesOut) {
        try {
            // 获取当前操作用户
            SysUserEntity currentUser = getCurrentUser();
            Date operateTime = new Date();

            // 检查是否有任何变化（商品或费用）
            boolean hasCommodityChanges = hasCommodityChanges(beforeSnapshot, afterSnapshot);
            boolean hasCostChanges = hasCostChanges(beforeSnapshot, afterSnapshot);

            // 如果有变化，则记录一条综合变更记录
            if (hasCommodityChanges || hasCostChanges) {
                SalesOutChangeRecord combinedRecord = buildCombinedChangeRecord(
                    salesOutId, docNo, annotation, 
                    beforeSnapshot, afterSnapshot, currentUser, operateTime, salesOut
                );
                changeRecordService.save(combinedRecord);
                log.info("记录综合变化（商品和费用）- 销售出库单: {}, 操作类型: {}", docNo, annotation.type());
            }

        } catch (Exception e) {
            log.error("记录变化失败，销售出库单ID: {}", salesOutId, e);
        }
    }

    /**
     * 判断商品明细是否有变化
     */
    private boolean hasCommodityChanges(DataSnapshot before, DataSnapshot after) {
        if (before == null) {
            // 新增场景
            return CollectionUtil.isNotEmpty(after.getCommodities());
        }

        List<SalesOutDetail> beforeList = before.getCommodities();
        List<SalesOutDetail> afterList = after.getCommodities();

        // 数量不同
        if (beforeList.size() != afterList.size()) {
            return true;
        }

        // 比较每个商品的关键字段
        Map<String, CommodityKey> beforeMap = beforeList.stream()
            .collect(Collectors.toMap(
                d -> generateCommodityKey(d),
                d -> new CommodityKey(d),
                (v1, v2) -> v1
            ));

        for (SalesOutDetail afterDetail : afterList) {
            String key = generateCommodityKey(afterDetail);
            CommodityKey beforeKey = beforeMap.get(key);
            
            if (beforeKey == null || !beforeKey.equals(new CommodityKey(afterDetail))) {
                return true;
            }
        }

        return false;
    }

    /**
     * 判断费用是否有变化
     */
    private boolean hasCostChanges(DataSnapshot before, DataSnapshot after) {
        if (before == null) {
            // 新增场景
            return CollectionUtil.isNotEmpty(after.getCosts());
        }

        List<OrderCost> beforeList = before.getCosts();
        List<OrderCost> afterList = after.getCosts();

        // 数量不同
        if (beforeList.size() != afterList.size()) {
            return true;
        }

        // 比较每个费用
        Map<Long, BigDecimal> beforeMap = beforeList.stream()
            .collect(Collectors.toMap(
                OrderCost::getCostClauseId,
                OrderCost::getMoney,
                (v1, v2) -> v1
            ));

        for (OrderCost afterCost : afterList) {
            BigDecimal beforeMoney = beforeMap.get(afterCost.getCostClauseId());
            if (beforeMoney == null || !NumberUtil.equals(beforeMoney, afterCost.getMoney())) {
                return true;
            }
        }

        return false;
    }

    /**
     * 生成商品唯一键
     */
    private String generateCommodityKey(SalesOutDetail detail) {
        return detail.getCommodityId() + "-" + detail.getSpecsId();
    }

    /**
     * 构建综合变更记录（同时包含商品和费用变化）
     */
    private SalesOutChangeRecord buildCombinedChangeRecord(Long salesOutId, String docNo,
                                                           SalesOutChangeTracking annotation,
                                                           DataSnapshot beforeSnapshot,
                                                           DataSnapshot afterSnapshot,
                                                           SysUserEntity currentUser,
                                                           Date operateTime,
                                                           SalesOut salesOut) {
        SalesOutChangeRecord record = new SalesOutChangeRecord();
        record.setSalesOutId(salesOutId);
        record.setDocNo(docNo);
        record.setOrderTime(salesOut.getOrderTime());
        record.setOperationType(annotation.type().name());
        // 新的变更类型，表示同时包含商品和费用的变更
        record.setChangeType("COMBINED");
        record.setOperateTime(operateTime);

        if (currentUser != null) {
            record.setOperatorId(currentUser.getUserId());
            record.setOperatorName(currentUser.getUsername());
        }

        // 设置生产者和客户信息
        if (salesOut != null) {
            record.setProducerId(salesOut.getProducerId());
            record.setCustomerId(salesOut.getCustomerId());
            record.setCustomerName(salesOut.getCustomerName());
        }

        // 构建综合的前后数据和变更摘要
        CombinedChangeData combinedBeforeData = new CombinedChangeData();
        CombinedChangeData combinedAfterData = new CombinedChangeData();
        
        // 设置变更前数据
        if (beforeSnapshot != null) {
            combinedBeforeData.setCommodities(simplifyDetails(beforeSnapshot.getCommodities()));
            combinedBeforeData.setCosts(simplifyCosts(beforeSnapshot.getCosts()));
            record.setBeforeData(JSON.toJSONString(combinedBeforeData));
        }
        
        // 设置变更后数据
        combinedAfterData.setCommodities(simplifyDetails(afterSnapshot.getCommodities()));
        combinedAfterData.setCosts(simplifyCosts(afterSnapshot.getCosts()));
        record.setAfterData(JSON.toJSONString(combinedAfterData));
        
        // 设置变更摘要
        record.setChangeSummary(buildCombinedChangeSummary(beforeSnapshot, afterSnapshot));

        return record;
    }

    /**
     * 构建综合变更摘要
     */
    private String buildCombinedChangeSummary(DataSnapshot before, DataSnapshot after) {
        StringBuilder summary = new StringBuilder();
        
        // 商品变更摘要
        boolean hasCommodityChanges = hasCommodityChanges(before, after);
        if (hasCommodityChanges) {
            if (before == null) {
                summary.append(String.format("新增 %d 个商品明细；", after.getCommodities().size()));
            } else {
                int beforeCount = before.getCommodities().size();
                int afterCount = after.getCommodities().size();
                if (beforeCount != afterCount) {
                    summary.append(String.format("商品明细数量变化：%d -> %d；", beforeCount, afterCount));
                } else {
                    summary.append("商品明细内容发生变化；");
                }
            }
        }
        
        // 费用变更摘要
        boolean hasCostChanges = hasCostChanges(before, after);
        if (hasCostChanges) {
            if (before == null) {
                BigDecimal totalMoney = after.getCosts().stream()
                    .map(OrderCost::getMoney)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
                summary.append(String.format("新增 %d 项费用，总计：%.2f", after.getCosts().size(), totalMoney));
            } else {
                BigDecimal beforeTotal = before.getCosts().stream()
                    .map(OrderCost::getMoney)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal afterTotal = after.getCosts().stream()
                    .map(OrderCost::getMoney)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
                summary.append(String.format("费用变化：%.2f -> %.2f", beforeTotal, afterTotal));
            }
        }
        
        return summary.toString();
    }

    /**
     * 简化商品明细数据（只保留关键字段）
     */
    private List<Map<String, Object>> simplifyDetails(List<SalesOutDetail> details) {
        if (CollectionUtil.isEmpty(details)) {
            return new ArrayList<>();
        }

        return details.stream().map(d -> {
            Map<String, Object> map = new HashMap<>();
            map.put("commodityId", d.getCommodityId());
            map.put("commodityName", d.getCommodityName());
            map.put("specsId", d.getSpecsId());
            map.put("specsName", d.getSpecsName());
            map.put("number", d.getNumber());
            map.put("weight", d.getWeight());
            map.put("unitPrice", d.getUnitPrice());
            map.put("money", d.getMoney());
            map.put("cost", d.getCost());
            return map;
        }).collect(Collectors.toList());
    }

    /**
     * 简化费用数据（只保留关键字段）
     */
    private List<Map<String, Object>> simplifyCosts(List<OrderCost> costs) {
        if (CollectionUtil.isEmpty(costs)) {
            return new ArrayList<>();
        }

        return costs.stream().map(c -> {
            Map<String, Object> map = new HashMap<>();
            map.put("costClauseId", c.getCostClauseId());
            map.put("costClauseName", c.getCostClauseName());
            map.put("money", c.getMoney());
            return map;
        }).collect(Collectors.toList());
    }

    /**
     * 获取当前登录用户
     */
    private SysUserEntity getCurrentUser() {
        try {
            return (SysUserEntity) SecurityUtils.getSubject().getPrincipal();
        } catch (Exception e) {
            log.debug("获取当前用户失败", e);
            return null;
        }
    }

    /**
     * 数据快照类
     */
    @Data
    private static class DataSnapshot {
        private List<SalesOutDetail> commodities = new ArrayList<>();
        private List<OrderCost> costs = new ArrayList<>();
    }

    /**
     * 商品关键字段类（用于比较）
     */
    @Data
    private static class CommodityKey {
        private Integer number;
        private BigDecimal weight;
        private BigDecimal unitPrice;
        private BigDecimal money;
        private BigDecimal cost;

        public CommodityKey(SalesOutDetail detail) {
            this.number = detail.getNumber();
            this.weight = detail.getWeight();
            this.unitPrice = detail.getUnitPrice();
            this.money = detail.getMoney();
            this.cost = detail.getCost();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            CommodityKey that = (CommodityKey) o;
            return Objects.equals(number, that.number) &&
                   NumberUtil.equals(weight, that.weight) &&
                   NumberUtil.equals(unitPrice, that.unitPrice) &&
                   NumberUtil.equals(money, that.money) &&
                   NumberUtil.equals(cost, that.cost);
        }

        @Override
        public int hashCode() {
            return Objects.hash(number, weight, unitPrice, money, cost);
        }
    }

    /**
     * 综合变更数据类（用于同时存储商品和费用信息）
     */
    @Data
    private static class CombinedChangeData {
        private List<Map<String, Object>> commodities = new ArrayList<>();
        private List<Map<String, Object>> costs = new ArrayList<>();
    }
}
